#pragma once
#include <iostream>
#include "Util.hpp"
#include "Log.hpp"
#include <sstream>
#include <string>
#include <vector>
#include <unordered_map>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <algorithm>
#include <fcntl.h>
#include <sys/sendfile.h>
#include<sys/wait.h>

#define SEP ": "

// 返回的报头中换行符采用\r\n
#define LINE_END "\r\n"

#define OK 200
#define BAD_REQUEST 400
#define Unauthorized 401
#define NOT_FOUND 404
#define SERVER_ERROR 500

#define WEB_ROOT "root"
#define HOME_PAGE "index.html"
#define PAGE_404 "404page.html"
#define SIGN_IN "sign_in.html"

#define HTTP_VERSION "HTTP/1.0"

static std::string CodeToDesc(int code)
{
    switch (code)
    {
    case 200:
        return "OK";
    case 404:
        return "NOT FOUND";
    default:
        break;
    }
    return "";
}

static std::string SuffixToDesc(std::string suf)
{
    static std::unordered_map<std::string, std::string> suftodesc = {
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "application/xml"}};

    auto desc = suftodesc.find(suf);
    if (desc != suftodesc.end())
    {
        return desc->second;
    }

    // 返回默认值
    return "text/html";
}

class Httprequest
{
public:
    std::string request_line;                // 请求报文
    std::vector<std::string> request_hander; // 响应报文
    std::string blank;                       // 空行
    std::string body;                        // 正文

    // 请求报文拆分
    std::string method;
    std::string uri;
    std::string version;

    //cookie
    std::string user;
    std::string passwd;

    // 对uri进行拆分
    std::string path;
    std::string query_string;

    std::string suffix; // 请求资源格式

    // 响应报文拆分
    int content_length = 0;
    std::unordered_map<std::string, std::string> hander_kv;

    size_t size;      // 请求文件的大小
    bool cgi = false; // 是否需要对数据进行处理
};

class Httpresponse
{
public:
    std::string response_line;
    std::vector<std::string> response_hander;
    std::string blank=LINE_END;
    std::string body;

    int status_code;
    int fd=-1;
};

class EndPoint
{
private:
    int sock;
    Httprequest request;
    Httpresponse response;
    bool quit;

private:
    bool RecvHttpRequestLine()
    {
        auto &line = request.request_line;
        if (Util::ReadLine(sock, line) > 0) // 读取请求报文
        {
            line.resize(line.size() - 1); // 去除\n
            LOG(INFO, request.request_line);
        }
        else
        {
            quit = true;
        }
        return quit;
    }

    bool RecvHttpRequestHeader()
    {
        std::string line;
        while (true)
        {
            line.clear();
            if (Util::ReadLine(sock, line) <= 0)
            {
                quit = true;
                break;
            }
            if (line == "\n")
            {
                request.blank = line;
                break;
            }
            line.resize(line.size() - 1);
            request.request_hander.push_back(line);
            LOG(INFO, line);
        }
        return quit;
    }

    void DivideRequestLine()
    {
        auto &line = request.request_line;
        std::stringstream str(line);
        str >> request.method >> request.uri >> request.version;
        auto &method = request.method;

        std::transform(method.begin(), method.end(), method.begin(), toupper); // 转换为大写
    }
    
    bool GetCookie()
    {
        const auto&it=request.hander_kv.find("Cookie");
        if(it!=request.hander_kv.end())
        {
            std::string cookie=it->second;
            Util::CutString(cookie,request.user,request.passwd,"|");
            return true;
        }
        return false;
    }

    void DivideRequestHander()
    {
        std::string key;
        std::string value;
        for (auto &str : request.request_hander)
        {
            if (Util::CutString(str, key, value, SEP))
            {
                request.hander_kv[key] = value;
            }
        }
    }

    bool IsNeedDivideRequestBody()
    {
        if (request.method == "POST")
        {
            const auto &length_pair = request.hander_kv.find("Content-Length");
            if (length_pair != request.hander_kv.end())
            {
                request.content_length = stoi(length_pair->second);
                return true;
            }
        }
        return false;
    }

    bool RecvRequestBody()
    {
        if (IsNeedDivideRequestBody())
        {
            int len = request.content_length;
            auto &body = request.body;
            char ch = 0;
            while (len)
            {
                ssize_t s = recv(sock, &ch, 1, 0);
                if (s > 0)
                {
                    len--;
                    body.push_back(ch);
                }
                else
                {
                    quit = true;
                    break;
                }
            }
            LOG(INFO, body);
            return quit;
        }
    }

    int ProcessNonCgi()
    {
        response.fd = open(request.path.c_str(), O_RDONLY);
        if (response.fd >= 0)
        {
            LOG(INFO,request.path+" open success");
            return 200;
        }
        return 404;
    }

    int ProcessCgi()
    {
        int code=OK;
        LOG(INFO, "process cgi mthod!");
        auto&method=request.method;
        auto&path=request.path;

        auto&query_string=request.query_string;
        auto&req_body=request.body;
        auto&rep_body=response.body;

        std::string query_string_env;
        std::string method_env;
        std::string content_length_env;

        int input[2];
        int output[2];

        if(pipe(input)<0)
        {
            LOG(ERROR,"pipe input error");
            code = SERVER_ERROR;
            return code;
        }
        if(pipe(output)<0)
        {
            LOG(ERROR,"pipe output error");
            code = SERVER_ERROR;
            return code;
        }
        pid_t id=fork();
        if(id==0)
        {
            close(input[0]);
            close(output[1]);
            method_env="METHOD=";
            method_env+=method;

            putenv((char*)method_env.c_str());//c_str为constchar*，需要参数为char*

            if(method=="GET")
            {
                query_string_env="QUERY_STRING=";
                query_string_env+=query_string;
                putenv((char*)query_string_env.c_str());
            }
            else if(method=="POST")
            {
                content_length_env="Content-Length=";
                content_length_env+=std::to_string(request.content_length);
                putenv((char*)content_length_env.c_str());
            }

            auto&user=request.user;
            auto&passwd=request.passwd;
            std::string user_env="user=";
            user_env+=user;
            std::string passwd_env="passwd=";
            passwd_env+=passwd;
            putenv((char*)user_env.c_str());
            putenv((char*)passwd_env.c_str());

            dup2(input[1],1);
            dup2(output[0],0);

            execl(path.c_str(),path.c_str(),nullptr);
            exit(1);

        }
        else if(id>0)
        {
            close(input[1]);
            close(output[0]);
            
            if(method=="POST")
            {
                int size=0;//本次读取字节数
                int total=0;//一共读取字节数
                int content_length=request.content_length;

                const char *start = req_body.c_str();
                while (total < content_length && (size = write(output[1], start + total, req_body.size() - total)) > 0)
                {//防止一次读不完
                    total += size;
                }
            }

            char ch=0;
            while(read(input[0],&ch,1)>0)
            {
                rep_body.push_back(ch);
            }
            int status=0;
            pid_t ret=waitpid(id,&status,0);
            if(ret==id)
            {
                if(WIFEXITED(status))//判断子进程是防止正常退出
                {
                    if(WEXITSTATUS(status)==0)//判断子进程退出码
                    {
                        code=OK;
                    }
                    else
                    {
                        code=BAD_REQUEST;
                    }
                }
                else
                {
                    code=SERVER_ERROR;
                }
            }
            close(input[0]);
            close(output[1]);
        }
        else
        {
            LOG(ERROR,"fork error");
            code=NOT_FOUND;
        }

        return code;
    }

    void HandlerError(std::string page)
    {
        request.cgi=false;
        response.fd=open(page.c_str(),O_RDONLY);
        if(response.fd>0)
        {
            struct stat st;
            stat(page.c_str(),&st);
            request.size=st.st_size;
            
            std::string line="Content-Type: text/html";
            line+=LINE_END;
            response.response_hander.push_back(line);

            line="Content-Length: ";
            line+=std::to_string(request.size);
            line+=LINE_END;
            response.response_hander.push_back(line);
        }

    }

    void BuildOkResponse()
    {
        std::string line="Content-Type: ";
        line+=SuffixToDesc(request.suffix);
        line+=LINE_END;
        response.response_hander.push_back(line);

        line="Content-Length: ";
        if(request.cgi)
        {
            line+=std::to_string(response.body.size());
        }
        else
        {
            line+=std::to_string(request.size);
        }
        line+=LINE_END;
        response.response_hander.push_back(line);

    }

    void BuildHttpResponseHelper()
    {
        auto&code=response.status_code;
        auto&rep_line=response.response_line;

        rep_line=HTTP_VERSION;
        rep_line+=" ";
        rep_line+=std::to_string(code);
        rep_line+=" ";
        rep_line+=CodeToDesc(code);
        rep_line+=LINE_END;

        std::string path=WEB_ROOT;
        path+="/";

        switch (code)
        {
        case OK:
            BuildOkResponse();
            break;
        case BAD_REQUEST:
            // path+=PAGE_400;
            // HandlerError(path);
            break;
        case Unauthorized:
            path+=SIGN_IN;
            HandlerError(path);
            break;
        case NOT_FOUND:
            path+=PAGE_404;
            HandlerError(path);
            break;
        case SERVER_ERROR:
            // path+=PAGE_500;
            // HandlerError(path);
            break;
        default:
            break;
        }
    }

public:
    void RecvRequset()
    {
        if (!RecvHttpRequestLine() && !RecvHttpRequestHeader())
        {
            DivideRequestLine();
            DivideRequestHander();
            RecvRequestBody();
            
        }
        else
        {
            LOG(WARNING,"Recv HttpRequest Error");
        }
    }


    void BuildHttpResponse() // 构建响应
    {
        std::string _path;
        struct stat st;
        size_t found = 0;
        auto &code = response.status_code;

        if (request.method != "GET" && request.method != "POST")
        {
            // 非法请求
            LOG(WARNING, "method "+request.method+" is not right");
            code = BAD_REQUEST;
            goto END;
        }
        if (request.method == "GET")
        {
            if (request.uri.find("?") != std::string::npos) // 判断有误参数
            {
                // 传参了，因此需要对传过来的参数进行数据处理
                request.cgi = true;
                Util::CutString(request.uri, request.path, request.query_string, "?");
            }
            else
            {
                // 无参数，uri就是path
                request.path = request.uri;
            }
        }
        else if (request.method == "POST")
        {
            request.path = request.uri;
            
            if(request.body.size())
                request.cgi = true;
        }

        // 确保路径在root下
        _path = request.path;
        request.path = WEB_ROOT;
        request.path += _path;

        // 如果路径指向的是个目录
        if (request.path[request.path.size() - 1] == '/')
        {
            request.path += HOME_PAGE; // 让其返回index.html
        }

        if (stat(request.path.c_str(), &st) == 0) // 判断路径指向的文件是否存在
        {                                         // 获取不到文件信息就说明文件不存在
            if (S_ISDIR(st.st_mode))
            {
                // 重新做一次判断，检测其是否为目录，
                // 因为path可能是路径，但是不是以/结尾
                request.path += "/";
                request.path += HOME_PAGE;
                stat(request.path.c_str(), &st);
            }

            // 判断文件是否为可执行程序
            if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {
                request.cgi = true;
            }
            request.size = st.st_size;
        }
        else
        {
            // 找不到文件
            LOG(WARNING, request.path + " Not Found");
            code = NOT_FOUND;
            goto END;
        }

        // 判断请求资源的后缀
        found = request.path.rfind('.');
        if (found != std::string::npos)
        {
            request.suffix = request.path.substr(found);
        }
        else
        {
            request.suffix = ".html";
        }

        if (!GetCookie()&&!(request.path=="root/index.html"))
        {
            code = Unauthorized;
        }

        if (request.cgi)
        {
            code = ProcessCgi();
        }
        else
        {
            code = ProcessNonCgi();
        }
    END:
            BuildHttpResponseHelper();
    }

    void SendHttpResponse()
    {
        send(sock, response.response_line.c_str(), response.response_line.size(), 0);

        for (auto &it : response.response_hander)
        {
            send(sock, it.c_str(), it.size(), 0);
        }

         send(sock, response.blank.c_str(), response.blank.size(), 0);

        if (request.cgi)
        {
            send(sock, response.body.c_str(), response.body.size(), 0);
        }
        else
        {
            sendfile(sock, response.fd, nullptr, request.size);
        }

        close(response.fd);
    }

    bool IsQuit()
    {
        return quit;
    }

public:
    EndPoint(int _sock)
        : sock(_sock), quit(false)
    {
    }

    ~EndPoint()
    {
        close(sock);
    }
};

class CallBack
{
public:
    CallBack()
    {}
    ~CallBack()
    {}
public:
    void operator()(int sock)
    {
        HandlerRequest(sock);
    }

    void HandlerRequest(int sock)
    {
        LOG(INFO, "begin");
        EndPoint *ep = new EndPoint(sock);
        ep->RecvRequset();
        if(!ep->IsQuit())
        {
            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        }
        else
        {
            LOG(WARNING, "Recv Error, Stop Build And Send");
        }
        LOG(INFO, "end");
        delete ep;
    }
};